// UDP-服务端socket编程

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h> //using for IPPROTO_UDP
#include <arpa/inet.h>  //using for inet_addr()、htons()、htonl()

int main()
{
    // 1.创建套接字
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock_fd < 0)
    {
        perror("socket");
        return 0;
    }
    printf("sock_fd is %d\n",sock_fd);

    // 2.绑定地址信息
    struct sockaddr_in ser_addr;
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(38989);
    //ser_addr.sin_addr.s_addr = inet_addr("10.0.16.3");  
    ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0");  
    // 注意：源IP一定是私网IP
    // 或写成"0.0.0.0"，表示当前主机任一块网卡的IP

    int ret = bind(sock_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    if(ret < 0)
    {
        perror("bind");
        return 0;
    }
    
    // 循环收发
    while(1)
    {
        // 3.接收客户端套接字
        //  (1)创建buf缓冲区作为出参，保存客户端发来的数据
        //  (2)创建地址信息结构体作为出参，保存客户端的地址信息
        //  (3)调用recvfrom()阻塞接收
        //  (4)打印客户端数据
        //      输出客户端套接字的地址信息时：
        //      调用inet_ntoa()，将IP转为主机字节序的点分十进制
        //      调用ntohs()，将PORT转为主机字节序

        char buf[1024] = {0};  //buf一定要初始化；且buf使用时需保留一位用做'\0'
        struct sockaddr_in cli_addr;  //出参
        socklen_t cli_addr_size = sizeof(cli_addr);
        
        ssize_t recv_size = recvfrom(sock_fd, buf, sizeof(buf) - 1, 0 , (sockaddr*)&cli_addr, &cli_addr_size);
        if(recv_size < 0)
        {
            return 0;
        }
        
        // server <- client(ip/port)：数据
        printf("server <- client(%s/%d): %s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);

        // 4.发送数据（响应客户端的请求）
        //  (1)为buf缓冲区初始化数据
        //  (2)调用sendto()阻塞发送
        
        memset(buf, '\0', sizeof(buf));  //清空buf
        // 调用格式输出，向缓冲区写入：client <- server(ip/port): 数据
        sprintf(buf, "client <- server(%s/%d): GET!\n", inet_ntoa(ser_addr.sin_addr), ntohs(ser_addr.sin_port));

        sleep(1);
        ssize_t send_size = sendto(sock_fd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_addr_size);
        if(send_size < 0)
        {
            continue;
        }
    }

    // 关闭套接字
    close(sock_fd);

    return 0;
}
